home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / OWLSRC.PAK / BUTTONGA.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  14.4 KB  |  590 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1992, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   10.15  $
  6. //
  7. // Implementation of class TButtonGadget.
  8. //----------------------------------------------------------------------------
  9. #include <owl/pch.h>
  10. #if !defined(OWL_BUTTONGA_H)
  11. # include <owl/buttonga.h>
  12. #endif
  13. #if !defined(OWL_GADGETWI_H)
  14. # include <owl/gadgetwi.h>
  15. #endif
  16. #if !defined(OWL_CELARRAY_H)
  17. # include <owl/celarray.h>
  18. #endif
  19. #if !defined(OWL_UIHELPER_H)
  20. # include <owl/uihelper.h>
  21. #endif
  22. #if !defined(WINSYS_UIMETRIC_H)
  23. # include <winsys/uimetric.h>
  24. #endif
  25. #if !defined(WINSYS_SYSTEM_H)
  26. # include <winsys/system.h>
  27. #endif
  28.  
  29. OWL_DIAGINFO;
  30. DIAG_DECLARE_GROUP(OwlGadget);
  31.  
  32. //
  33. // Command enabler for button gadgets
  34. //
  35. class _OWLCLASS TButtonGadgetEnabler : public TCommandEnabler {
  36.   public:
  37.     TButtonGadgetEnabler(TWindow::THandle hReceiver, TButtonGadget* g)
  38.     :
  39.       TCommandEnabler(g->GetId(), hReceiver),
  40.       Gadget(g)
  41.     {
  42.     }
  43.  
  44.     // Override TCommandEnabler virtuals
  45.     //
  46.     void  Enable(bool);
  47.     void  SetText(const char far*);
  48.     void  SetCheck(int);
  49.  
  50.   protected:
  51.     TButtonGadget*  Gadget;
  52. };
  53.  
  54. //
  55. // Enable or disable the button gadget
  56. //
  57. void
  58. TButtonGadgetEnabler::Enable(bool enable)
  59. {
  60.   TCommandEnabler::Enable(enable);
  61.   Gadget->SetEnabled(enable);
  62. }
  63.  
  64. //
  65. // Handle the SetText request for a button gadget. Currently does nothing.
  66. //
  67. void
  68. TButtonGadgetEnabler::SetText(const char far*)
  69. {
  70. # pragma warn -ccc
  71. # pragma warn -rch
  72.   CHECK(true);
  73. # pragma warn .rch
  74. # pragma warn .ccc
  75. }
  76.  
  77. //
  78. // Set the check-state for the button gadget
  79. //
  80. void
  81. TButtonGadgetEnabler::SetCheck(int state)
  82. {
  83.   Gadget->SetButtonState(TButtonGadget::TState(state));
  84. }
  85.  
  86. //----------------------------------------------------------------------------
  87.  
  88. //
  89. // Construct a button gadget that loads its own bitmap resource
  90. //
  91. TButtonGadget::TButtonGadget(TResId glyphResIdOrIndex,
  92.                              int    id,
  93.                              TType  type,
  94.                              bool   enabled,
  95.                              TState state,
  96.                              bool   sharedGlyph)
  97. :
  98.   TGadget(id, ButtonUp),
  99.   SharingGlyph(sharedGlyph)
  100. {
  101.   // If we are sharing the glyph held by our window, then we won't need to load
  102.   // the resource
  103.   //
  104.   if (sharedGlyph) {
  105.     ResId = 0;
  106.     GlyphIndex = (int)(const char far*)glyphResIdOrIndex;
  107.     GlyphCount = 1;
  108.   }
  109.   else {
  110.     ResId = glyphResIdOrIndex.IsString() ?
  111.                 strnewdup(glyphResIdOrIndex) : (char far*)glyphResIdOrIndex;
  112.     GlyphIndex = -1;   // Need to get during BuildCelArray
  113.     GlyphCount = 4;
  114.   }
  115.  
  116.   CelArray = 0;
  117.   BitmapOrigin.x = BitmapOrigin.y = 0;
  118.   Type = type;
  119. //  Repeat = repeat;  
  120.   TrackMouse = true;
  121.   State = state;
  122.   NotchCorners = true;
  123.   Pressed = false;
  124.   SetEnabled(enabled);
  125.   SetShadowStyle(DoubleShadow);
  126.   SetAntialiasEdges(true);
  127.  
  128.   TRACEX(OwlGadget, OWL_CDLEVEL, "TButtonGadget constructed @" << (void*)this);
  129. }
  130.  
  131. //
  132. // Destruct this button gadget, freeing up allocated resources
  133. //
  134. TButtonGadget::~TButtonGadget()
  135. {
  136.   delete CelArray;
  137.   if (ResId.IsString())
  138.     delete[] (char far*)ResId;
  139.  
  140.   TRACEX(OwlGadget, OWL_CDLEVEL, "TButtonGadget destructed @" << (void*)this);
  141. }
  142.  
  143. //
  144. // Set the shadow style for this button. May be ignored on some 3d UI platforms
  145. //
  146. void
  147. TButtonGadget::SetShadowStyle(TShadowStyle shadowStyle)
  148. {
  149.   if (!TSystem::Has3dUI()) {
  150.     TBorders  borders;
  151.  
  152.     ShadowStyle = shadowStyle;
  153.     borders.Left = borders.Top = 2;
  154.     borders.Right = borders.Bottom = ShadowStyle + 1;
  155.     SetBorders(borders);
  156.   }
  157. }
  158.  
  159. //
  160. // Initiate a command enable for this button gadget
  161. //
  162. void
  163. TButtonGadget::CommandEnable()
  164. {
  165.   PRECONDITION(Window);
  166.  
  167.   // Must send, not post here, since a ptr to a temp is passed
  168.   //
  169.   // This might be called during idle processing before the
  170.   // HWND has created.  Therefore, confirm handle exists.
  171.   //
  172.   if (Window->GetHandle()) {
  173.     Window->HandleMessage(
  174.       WM_COMMAND_ENABLE,
  175.       0,
  176.       TParam2(&TButtonGadgetEnabler(*Window, this))
  177.     );
  178.   }
  179. }
  180.  
  181. //
  182. // Respond to a WM_SYSCOLORCHANGE, in this case to rebuild the CelArray with
  183. // possibly new 3d colors
  184. //
  185. void
  186. TButtonGadget::SysColorChange()
  187. {
  188. }
  189.  
  190. //
  191. // Perform an exclusive checking of this gadget by unchecking the neighboring
  192. // exclusive button gadgets
  193. //
  194. void
  195. TButtonGadget::CheckExclusively()
  196. {
  197.   PRECONDITION(Window);
  198.  
  199.   if (State != Down) {
  200.     if (Window) {
  201.       TButtonGadget*  first = 0;
  202.       TButtonGadget*  last = this;
  203.  
  204.       // Look for the start of the group in which this button is located
  205.       //
  206.       for (TGadget* g = Window->FirstGadget(); g && g != this;
  207.         g = Window->NextGadget(*g)) {
  208.  
  209.         TButtonGadget* bg = TYPESAFE_DOWNCAST(g, TButtonGadget);
  210.         if (!bg || bg->Type != Exclusive)
  211.           first = 0;
  212.  
  213.         else if (!first)
  214.           first = bg;
  215.       }
  216.  
  217.       if (!first)
  218.         first = this;
  219.  
  220.       // Look for the end of the group in which this button is located
  221.       //
  222.       while (Window->NextGadget(*last)) {
  223.         TButtonGadget* bg = TYPESAFE_DOWNCAST(Window->NextGadget(*last), TButtonGadget);
  224.         if (!bg || bg->Type != Exclusive)
  225.           break;
  226.  
  227.         else
  228.           last = bg;
  229.       }
  230.  
  231.       // Walk all the gadget between first and last (inclusive) and pop them up
  232.       // if they are down
  233.       //
  234.       while (true) {
  235.         if (first->State == Down) {
  236.           first->State = Up;
  237.           first->Invalidate();
  238.           first->Update();
  239.         }
  240.  
  241.         if (first == last)  // All done
  242.           break;
  243.  
  244.         first = TYPESAFE_DOWNCAST(first->NextGadget(), TButtonGadget);
  245.       }
  246.     }
  247.  
  248.     State = Down;
  249.   }
  250. }
  251.  
  252. //
  253. // Set the state of a button. Handles setting down on an exclusive button to
  254. // pop out the neighbors
  255. //
  256. void
  257. TButtonGadget::SetButtonState(TState newState)
  258. {
  259.   if (newState != State) {
  260.       if ((Type == Exclusive || Type == SemiExclusive)
  261.            && newState == Down)
  262.         CheckExclusively();
  263.       else
  264.         State = newState;
  265.     Invalidate();
  266.     Update();
  267.   }
  268. }
  269.  
  270. //
  271. // Set the bounding rect for this button gadget. Also takes care of
  272. // re-centering the glyph
  273. //
  274. void
  275. TButtonGadget::SetBounds(const TRect& bounds)
  276. {
  277.   PRECONDITION(Window);
  278.   TRACEX(OwlGadget, 1, "TButtonGadget::SetBounds() enter @" << this <<
  279.     " bounds " << bounds);
  280.  
  281.   TGadget::SetBounds(bounds);
  282.  
  283.   // Center the glyph within the inner bounds
  284.   //
  285.   TRect  innerRect;
  286.   GetInnerRect(innerRect);
  287.  
  288.   TSize  bitmapSize = CelArray ? CelArray->CelSize() : Window->GetCelArray().CelSize();
  289.   BitmapOrigin.x = innerRect.left + (innerRect.Width() - bitmapSize.cx) / 2;
  290.   BitmapOrigin.y = innerRect.top + (innerRect.Height() - bitmapSize.cy) / 2;
  291.   TRACEX(OwlGadget, 1, "TButtonGadget::SetBounds() leave @" << this <<
  292.     " bounds " << bounds);
  293. }
  294.  
  295. //
  296. // Find out how big this button gadget wants to be. Calculated using the base
  297. // size to get the borders, etc. plus the glyph size.
  298. //
  299. void
  300. TButtonGadget::GetDesiredSize(TSize& size)
  301. {
  302.   PRECONDITION(Window);
  303.   TRACEX(OwlGadget, 1, "TButtonGadget::GetDesiredSize() enter @"<< this <<
  304.     " size " << size);
  305.  
  306.   TGadget::GetDesiredSize(size);
  307.  
  308.   // Build our CelArray or CelArray entry if not done already
  309.   //
  310.   if (!CelArray && GlyphIndex < 0)
  311.     BuildCelArray();
  312.  
  313.   size += CelArray ? CelArray->CelSize() : Window->GetCelArray().CelSize();
  314.   TRACEX(OwlGadget, 1, "TButtonGadget::GetDesiredSize() leave @"<< this <<
  315.     " size " << size);
  316. }
  317.  
  318.  
  319. //
  320. // Virtual function responsible for supplying the dib for the glyph. Can be
  321. // overriden to get dib from elsewhere, cache it, map colors differently, etc.
  322. //
  323. // Returns 0 if no resource was specified for this button
  324. //
  325. TDib*
  326. TButtonGadget::GetGlyphDib()
  327. {
  328.   PRECONDITION(Window);
  329.  
  330.   if (ResId) {
  331.     TDib* glyph = new TDib(*Window->GetModule(), ResId);
  332.     glyph->MapUIColors( TDib::MapFace | TDib::MapText | TDib::MapShadow |
  333.       TDib::MapHighlight );
  334.     return glyph;
  335.   }
  336.   return 0;
  337. }
  338.  
  339. //
  340. // Virtual function responsible for releasing glyph dib as needed based on how
  341. // GetGlyphDib() got it (if different from new/delete).
  342. //
  343. void
  344. TButtonGadget::ReleaseGlyphDib(TDib* glyph)
  345. {
  346.   delete glyph;
  347. }
  348.  
  349. //
  350. // Build the CelArray member using the resource bitmap as the base glyph
  351. // CelArray may contain an existing cel array that should be deleted if
  352. // replaced.
  353. //
  354. // The CelArray and glyph painting can work in one of the following ways:
  355. //   1. ResId ctor is used, ...., glyph states are cached in this CelArray
  356. //   2. ResId ctor is used, CelArray holds single glyph and paints state on
  357. //      the fly
  358. //   3. ResId ctor is used, glyph is added to Window's CelArray and paints
  359. //      state on the fly
  360. //   4. glyphIndex ctor is used, uses Window's CelArray glyph and paints state
  361. //      on the fly
  362. //
  363. void
  364. TButtonGadget::BuildCelArray()
  365. {
  366.   PRECONDITION(Window);
  367.  
  368.   TDib*  glyph = GetGlyphDib();
  369.  
  370.   // Case 4, no resource of our own--using a cel in our Window's CelArray
  371.   //
  372.   if (!glyph)
  373.     return;
  374.  
  375.  
  376.   // Case 3, add (or replace old) our glyph to our Window's CelArray
  377.   //
  378.   TSize glyphSize(glyph->Width(), glyph->Height());
  379.   TCelArray& celArray = Window->GetCelArray(glyph->Width(), glyph->Height());
  380.  
  381.   if (SharingGlyph && (glyphSize == celArray.CelSize())) {
  382.     if (GlyphIndex >= 0)
  383.       celArray.Replace(GlyphIndex, *glyph);
  384.     else
  385.       GlyphIndex = celArray.Add(*glyph);
  386.   }
  387.   else {
  388.  
  389.     // Case 2, build a CelArray containing only the normal glyph state
  390.     //
  391.     delete CelArray;
  392.     CelArray = new TCelArray(*glyph, 1); //CelsTotal);
  393.     GlyphIndex = 0;
  394.     SharingGlyph = false;
  395.   }
  396.  
  397.   ReleaseGlyphDib(glyph);
  398.  
  399.   // Case 1, for compatibility for now...
  400.   //
  401. }
  402.  
  403. //
  404. // Perform all of the painting for this button gadget
  405. //
  406. void
  407. TButtonGadget::Paint(TDC& dc)
  408. {
  409.   PRECONDITION(Window);
  410.  
  411.   // Make sure our style matches our state, then paint our gadget borders
  412.   //
  413.   BorderStyle = (Pressed || State == Down) ? ButtonDn : ButtonUp;
  414.   PaintBorder(dc);
  415.  
  416.   // Determine which CelArray to paint from: ours or our Window's
  417.   //
  418.   TCelArray& celArray = CelArray ? *CelArray : Window->GetCelArray();
  419.  
  420.   // Get the inner rect to use as the button face
  421.   //
  422.   TRect  faceRect;
  423.   GetInnerRect(faceRect);
  424.  
  425.   // Calc the source rect from the celarray. The dest point of the glyph is
  426.   // relative to the face rect.
  427.   //
  428.   TRect  srcRect(celArray.CelRect(GlyphIndex));
  429.   TPoint dstPt(BitmapOrigin - faceRect.TopLeft());
  430.  
  431.   // Create a UI Face object for this button & let it paint the button face
  432.   //
  433.   TUIFace face(faceRect, celArray);
  434.  
  435.   if (!GetEnabled())
  436.     face.Paint(dc, srcRect, dstPt, TUIFace::Disabled, false);
  437.   else if (State == Indeterminate)
  438.     face.Paint(dc, srcRect, dstPt, TUIFace::Indeterm, Pressed);
  439.   else if (State == Down && !Pressed)
  440.     face.Paint(dc, srcRect, dstPt, TUIFace::Down, false);
  441.   else
  442.     face.Paint(dc, srcRect, dstPt, TUIFace::Normal, Pressed);
  443. }
  444.  
  445. //
  446. // Invalidate the rectangle used by this gadget to cause it to repaint
  447. //
  448. void
  449. TButtonGadget::Invalidate()
  450. {
  451.   InvalidateRect(TRect(0, 0, Bounds.Width(), Bounds.Height()), false);
  452. }
  453.  
  454. //
  455. // Begin button pressed state, repaint & enter menuselect state
  456. //
  457. void
  458. TButtonGadget::BeginPressed(TPoint&)
  459. {
  460.   Pressed = true;
  461.   Invalidate();
  462.   Update();
  463.   if (Window->GetHintMode() == TGadgetWindow::PressHints)
  464.     Window->SetHintCommand(GetId());
  465. }
  466.  
  467. //
  468. // Cancel pressed state, repaint & end menuselect state
  469. //
  470. void
  471. TButtonGadget::CancelPressed(TPoint&)
  472. {
  473.   Pressed = false;
  474.   Invalidate();
  475.   Update();
  476.   if (Window->GetHintMode() == TGadgetWindow::PressHints)
  477.     Window->SetHintCommand(-1);
  478. }
  479.  
  480. //
  481. // The action method called on a completed 'click', generates WM_COMMAND
  482. //
  483. // Invoked by mouse-up event inside the Gadget. Sets member data "Pressed"
  484. // to false, changes state for attribute buttons, and paints the button
  485. // in its current state.
  486. //
  487. // Command buttons just send the command
  488. // Exclusive buttons check themselves and uncheck neighbors.
  489. // NonExclusive buttons toggle their check state.
  490. // SemiExclusive uncheck neighbors on press, but can also be unpressed.
  491. //
  492. void
  493. TButtonGadget::Activate(TPoint& pt)
  494. {
  495.   switch (Type) {
  496.     case Exclusive:
  497.       if (State != Down)
  498.         CheckExclusively();
  499.       break;
  500.  
  501.     case NonExclusive:
  502.       State = State == Up ? Down : Up;
  503.       break;
  504.  
  505.     case SemiExclusive:
  506.       if (State != Down)
  507.         CheckExclusively();
  508.       else
  509.         State = Up;
  510.       break;
  511.   }
  512.  
  513.   // Unpress the button
  514.   //
  515.   CancelPressed(pt);
  516.  
  517.   // Send the associated command for all enabled buttons except for exclusive
  518.   // buttons that are being poped up
  519.   //
  520.   if (!((Type == Exclusive || Type == SemiExclusive) && State != Down) &&
  521.       GetEnabled())
  522.     Window->PostMessage(WM_COMMAND, GetId()); 
  523. }
  524.  
  525. //
  526. // Handle the mouse left button down & enter the pressed state
  527. //
  528. void
  529. TButtonGadget::LButtonDown(uint modKeys, TPoint& pt)
  530. {
  531.   TGadget::LButtonDown(modKeys, pt);
  532.   BeginPressed(pt);
  533. }
  534.  
  535. //
  536. // Mouse has entered this button, (not pressed). Show hints if that style
  537. // is enabled
  538. //
  539. void
  540. TButtonGadget::MouseEnter(uint modKeys, TPoint& pt)
  541. {
  542.   TGadget::MouseEnter(modKeys, pt);
  543.   if (Window->GetHintMode() == TGadgetWindow::EnterHints)
  544.     Window->SetHintCommand(GetId());
  545. }
  546.  
  547. //
  548. // Mouse has moved (back) into this gadget. Push or pull the button up & down
  549. // as needed.
  550. //
  551. void
  552. TButtonGadget::MouseMove(uint modKeys, TPoint& pt)
  553. {
  554.   TGadget::MouseMove(modKeys, pt);
  555.  
  556.   bool hit = PtIn(pt);
  557.   if (Pressed) {
  558.     if (!hit)
  559.       CancelPressed(pt);
  560.   }
  561.   else if (hit) {
  562.     BeginPressed(pt);
  563.   }
  564. }
  565.  
  566. //
  567. // Mouse has left this button, (not pressed). Hide hints if that style
  568. // is enabled
  569. //
  570. void
  571. TButtonGadget::MouseLeave(uint modKeys, TPoint& pt)
  572. {
  573.   TGadget::MouseLeave(modKeys, pt);
  574.   if (Window->GetHintMode() == TGadgetWindow::EnterHints)
  575.     Window->SetHintCommand(-1);
  576. }
  577.  
  578. //
  579. // Handle the mouse left button up & leave the pressed state. If the mouse is
  580. // still on this button, i.e. it is still pressed, then perform the button
  581. // action.
  582. //
  583. void
  584. TButtonGadget::LButtonUp(uint modKeys, TPoint& pt)
  585. {
  586.   TGadget::LButtonUp(modKeys, pt);
  587.   if (Pressed)
  588.     Activate(pt);
  589. }
  590.